/*
 * contrib/seg/seg.c
 *
 ******************************************************************************
  此文件包含可以绑定到 Postgres 后端并由后端在处理查询的过程中调用的例程。
  这些例程的调用格式由 Postgres 架构决定。
******************************************************************************/

#include "postgres.h"

#include <float.h>

#include "access/gist.h"
#include "access/stratnum.h"
#include "fmgr.h"

#include "segdata.h"


#define DatumGetSegP(X) ((SEG *) DatumGetPointer(X))
#define PG_GETARG_SEG_P(n) DatumGetSegP(PG_GETARG_DATUM(n))


/*
#define GIST_DEBUG
#define GIST_QUERY_DEBUG
*/

PG_MODULE_MAGIC;

/*
 * picksplit 方法的辅助数据结构。
 */
typedef struct
{
	float		center;
	OffsetNumber index;
	SEG		   *data;
} gseg_picksplit_item;

/*
** 输入/输出例程
*/
PG_FUNCTION_INFO_V1(seg_in);
PG_FUNCTION_INFO_V1(seg_out);
PG_FUNCTION_INFO_V1(seg_size);
PG_FUNCTION_INFO_V1(seg_lower);
PG_FUNCTION_INFO_V1(seg_upper);
PG_FUNCTION_INFO_V1(seg_center);

/*
** GiST 支持方法
*/
PG_FUNCTION_INFO_V1(gseg_consistent);
PG_FUNCTION_INFO_V1(gseg_compress);
PG_FUNCTION_INFO_V1(gseg_decompress);
PG_FUNCTION_INFO_V1(gseg_picksplit);
PG_FUNCTION_INFO_V1(gseg_penalty);
PG_FUNCTION_INFO_V1(gseg_union);
PG_FUNCTION_INFO_V1(gseg_same);
static Datum fc_gseg_leaf_consistent(Datum fc_key, Datum fc_query, StrategyNumber fc_strategy);
static Datum fc_gseg_internal_consistent(Datum fc_key, Datum fc_query, StrategyNumber fc_strategy);
static Datum fc_gseg_binary_union(Datum fc_r1, Datum fc_r2, int *fc_sizep);


/*
** R-tree 支持函数
*/
PG_FUNCTION_INFO_V1(seg_same);
PG_FUNCTION_INFO_V1(seg_contains);
PG_FUNCTION_INFO_V1(seg_contained);
PG_FUNCTION_INFO_V1(seg_overlap);
PG_FUNCTION_INFO_V1(seg_left);
PG_FUNCTION_INFO_V1(seg_over_left);
PG_FUNCTION_INFO_V1(seg_right);
PG_FUNCTION_INFO_V1(seg_over_right);
PG_FUNCTION_INFO_V1(seg_union);
PG_FUNCTION_INFO_V1(seg_inter);
static void fc_rt_seg_size(SEG *fc_a, float *fc_size);

/*
** 各种操作符
*/
PG_FUNCTION_INFO_V1(seg_cmp);
PG_FUNCTION_INFO_V1(seg_lt);
PG_FUNCTION_INFO_V1(seg_le);
PG_FUNCTION_INFO_V1(seg_gt);
PG_FUNCTION_INFO_V1(seg_ge);
PG_FUNCTION_INFO_V1(seg_different);

/*
** 辅助函数
*/
static int	fc_restore(char *fc_s, float fc_val, int fc_n);


/*****************************************************************************
 * 输入/输出函数
 *****************************************************************************/

Datum seg_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	SEG		   *fc_result = palloc(sizeof(SEG));

	seg_scanner_init(fc_str);

	if (seg_yyparse(fc_result) != 0)
		seg_yyerror(fc_result, "bogus input");

	seg_scanner_finish();

	PG_RETURN_POINTER(fc_result);
}

Datum seg_out(PG_FUNCTION_ARGS)
{
	SEG		   *fc_seg = PG_GETARG_SEG_P(0);
	char	   *fc_result;
	char	   *fc_p;

	fc_p = fc_result = (char *) palloc(40);

	if (fc_seg->l_ext == '>' || fc_seg->l_ext == '<' || fc_seg->l_ext == '~')
		fc_p += sprintf(fc_p, "%c", fc_seg->l_ext);

	if (fc_seg->lower == fc_seg->upper && fc_seg->l_ext == fc_seg->u_ext)
	{
		/*
		 * 表示此区间是通过 seg_in 从一个单一点构建的
		 */
		fc_p += fc_restore(fc_p, fc_seg->lower, fc_seg->l_sigd);
	}
	else
	{
		if (fc_seg->l_ext != '-')
		{
			/* 如果存在，则打印下边界 */
			fc_p += fc_restore(fc_p, fc_seg->lower, fc_seg->l_sigd);
			fc_p += sprintf(fc_p, " ");
		}
		fc_p += sprintf(fc_p, "..");
		if (fc_seg->u_ext != '-')
		{
			/* 如果存在，则打印上边界 */
			fc_p += sprintf(fc_p, " ");
			if (fc_seg->u_ext == '>' || fc_seg->u_ext == '<' || fc_seg->l_ext == '~')
				fc_p += sprintf(fc_p, "%c", fc_seg->u_ext);
			fc_p += fc_restore(fc_p, fc_seg->upper, fc_seg->u_sigd);
		}
	}

	PG_RETURN_CSTRING(fc_result);
}

Datum seg_center(PG_FUNCTION_ARGS)
{
	SEG		   *fc_seg = PG_GETARG_SEG_P(0);

	PG_RETURN_FLOAT4(((float) fc_seg->lower + (float) fc_seg->upper) / 2.0);
}

Datum seg_lower(PG_FUNCTION_ARGS)
{
	SEG		   *fc_seg = PG_GETARG_SEG_P(0);

	PG_RETURN_FLOAT4(fc_seg->lower);
}

Datum seg_upper(PG_FUNCTION_ARGS)
{
	SEG		   *fc_seg = PG_GETARG_SEG_P(0);

	PG_RETURN_FLOAT4(fc_seg->upper);
}


/*****************************************************************************
 *						   GiST 函数
 *****************************************************************************/

/*
** 针对段的 GiST 一致性方法
** 如果所有数据项 x 在入口下，谓词 x op query == false，则应返回 false，
** 其中 op 是对应于 pg_amop 表中的策略的操作符。
*/
Datum gseg_consistent(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	Datum		fc_query = PG_GETARG_DATUM(1);
	StrategyNumber fc_strategy = (StrategyNumber) PG_GETARG_UINT16(2);

	/* Oid		subtype = PG_GETARG_OID(3); */
	bool	   *fc_recheck = (bool *) PG_GETARG_POINTER(4);

	/* 由此函数服务的所有案例都是精确的 */
	*fc_recheck = false;

	/*
	 * 如果入口不是叶子，使用 gseg_internal_consistent，否则使用
	 * gseg_leaf_consistent
	 */
	if (GIST_LEAF(fc_entry))
		return fc_gseg_leaf_consistent(fc_entry->key, fc_query, fc_strategy);
	else
		return fc_gseg_internal_consistent(fc_entry->key, fc_query, fc_strategy);
}

/*
** 针对段的 GiST 并集方法
** 返回包含 entryvec 中所有条目的最小边界段
*/
Datum gseg_union(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	int		   *fc_sizep = (int *) PG_GETARG_POINTER(1);
	int			fc_numranges,
				fc_i;
	Datum		fc_out = 0;
	Datum		fc_tmp;

#ifdef GIST_DEBUG
	fprintf(stderr, "union\n");
#endif

	fc_numranges = fc_entryvec->n;
	fc_tmp = fc_entryvec->vector[0].key;
	*fc_sizep = sizeof(SEG);

	for (fc_i = 1; fc_i < fc_numranges; fc_i++)
	{
		fc_out = fc_gseg_binary_union(fc_tmp, fc_entryvec->vector[fc_i].key, fc_sizep);
		fc_tmp = fc_out;
	}

	PG_RETURN_DATUM(fc_out);
}

/*
** 针对段的 GiST 压缩和解压方法
** 不做任何事情。
*/
Datum gseg_compress(PG_FUNCTION_ARGS)
{
	PG_RETURN_POINTER(PG_GETARG_POINTER(0));
}

Datum gseg_decompress(PG_FUNCTION_ARGS)
{
	PG_RETURN_POINTER(PG_GETARG_POINTER(0));
}

/*
** 针对段的 GiST 罚金方法
** 如 R-tree 论文中所述，我们使用面积变化作为我们的罚金指标
*/
Datum gseg_penalty(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
	GISTENTRY  *fc_newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
	float	   *fc_result = (float *) PG_GETARG_POINTER(2);
	SEG		   *fc_ud;
	float		fc_tmp1,
				fc_tmp2;

	fc_ud = DatumGetSegP(DirectFunctionCall2(seg_union,
										  fc_origentry->key,
										  fc_newentry->key));
	fc_rt_seg_size(fc_ud, &fc_tmp1);
	fc_rt_seg_size(DatumGetSegP(fc_origentry->key), &fc_tmp2);
	*fc_result = fc_tmp1 - fc_tmp2;

#ifdef GIST_DEBUG
	fprintf(stderr, "penalty\n");
	fprintf(stderr, "\t%g\n", *fc_result);
#endif

	PG_RETURN_POINTER(fc_result);
}

/*
 * gseg_picksplit_item 的比较函数：按中心排序。
 */
static int fc_gseg_picksplit_item_cmp(const void *fc_a, const void *fc_b)
{
	const gseg_picksplit_item *fc_i1 = (const gseg_picksplit_item *) fc_a;
	const gseg_picksplit_item *fc_i2 = (const gseg_picksplit_item *) fc_b;

	if (fc_i1->center < fc_i2->center)
		return -1;
	else if (fc_i1->center == fc_i2->center)
		return 0;
	else
		return 1;
}

/*
 * 针对段的 GiST PickSplit 方法
 *
 * 我们曾经在这里使用 Guttman 的分割算法，但由于数据是 1-D
 * 直接按中心点对段进行排序并在中间拆分更简单且更稳健。
 */
Datum gseg_picksplit(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *fc_v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
	int			fc_i;
	SEG		   *fc_seg,
			   *fc_seg_l,
			   *fc_seg_r;
	gseg_picksplit_item *fc_sort_items;
	OffsetNumber *fc_left,
			   *fc_right;
	OffsetNumber fc_maxoff;
	OffsetNumber fc_firstright;

#ifdef GIST_DEBUG
	fprintf(stderr, "picksplit\n");
#endif

	/* entryvec->vector[] 中有效项索引为 1..maxoff */
	fc_maxoff = fc_entryvec->n - 1;

	/*
	 * 准备辅助数组并对其进行排序。
	 */
	fc_sort_items = (gseg_picksplit_item *)
		palloc(fc_maxoff * sizeof(gseg_picksplit_item));
	for (fc_i = 1; fc_i <= fc_maxoff; fc_i++)
	{
		fc_seg = DatumGetSegP(fc_entryvec->vector[fc_i].key);
		/* 中心计算以避免可能的溢出 */
		fc_sort_items[fc_i - 1].center = fc_seg->lower * 0.5f + fc_seg->upper * 0.5f;
		fc_sort_items[fc_i - 1].index = fc_i;
		fc_sort_items[fc_i - 1].data = fc_seg;
	}
	qsort(fc_sort_items, fc_maxoff, sizeof(gseg_picksplit_item),
		  fc_gseg_picksplit_item_cmp);

	/* 排序项在 "firstright" 下面将进入左侧 */
	fc_firstright = fc_maxoff / 2;

	fc_v->spl_left = (OffsetNumber *) palloc(fc_maxoff * sizeof(OffsetNumber));
	fc_v->spl_right = (OffsetNumber *) palloc(fc_maxoff * sizeof(OffsetNumber));
	fc_left = fc_v->spl_left;
	fc_v->spl_nleft = 0;
	fc_right = fc_v->spl_right;
	fc_v->spl_nright = 0;

	/*
	 * 将段发射到左输出页面，并计算其边界框。
	 */
	fc_seg_l = (SEG *) palloc(sizeof(SEG));
	memcpy(fc_seg_l, fc_sort_items[0].data, sizeof(SEG));
	*fc_left++ = fc_sort_items[0].index;
	fc_v->spl_nleft++;
	for (fc_i = 1; fc_i < fc_firstright; fc_i++)
	{
		Datum		fc_sortitem = PointerGetDatum(fc_sort_items[fc_i].data);

		fc_seg_l = DatumGetSegP(DirectFunctionCall2(seg_union,
												 PointerGetDatum(fc_seg_l),
												 fc_sortitem));
		*fc_left++ = fc_sort_items[fc_i].index;
		fc_v->spl_nleft++;
	}

	/*
	 * 右页面同样处理。
	 */
	fc_seg_r = (SEG *) palloc(sizeof(SEG));
	memcpy(fc_seg_r, fc_sort_items[fc_firstright].data, sizeof(SEG));
	*fc_right++ = fc_sort_items[fc_firstright].index;
	fc_v->spl_nright++;
	for (fc_i = fc_firstright + 1; fc_i < fc_maxoff; fc_i++)
	{
		Datum		fc_sortitem = PointerGetDatum(fc_sort_items[fc_i].data);

		fc_seg_r = DatumGetSegP(DirectFunctionCall2(seg_union,
												 PointerGetDatum(fc_seg_r),
												 fc_sortitem));
		*fc_right++ = fc_sort_items[fc_i].index;
		fc_v->spl_nright++;
	}

	fc_v->spl_ldatum = PointerGetDatum(fc_seg_l);
	fc_v->spl_rdatum = PointerGetDatum(fc_seg_r);

	PG_RETURN_POINTER(fc_v);
}

/*
** 相等方法
*/
Datum gseg_same(PG_FUNCTION_ARGS)
{
	bool	   *fc_result = (bool *) PG_GETARG_POINTER(2);

	if (DirectFunctionCall2(seg_same, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)))
		*fc_result = true;
	else
		*fc_result = false;

#ifdef GIST_DEBUG
	fprintf(stderr, "same: %s\n", (*fc_result ? "TRUE" : "FALSE"));
#endif

	PG_RETURN_POINTER(fc_result);
}

/*
** 支持例程
*/
static Datum fc_gseg_leaf_consistent(Datum fc_key, Datum fc_query, StrategyNumber fc_strategy)
{
	Datum		fc_retval;

#ifdef GIST_QUERY_DEBUG
	fprintf(stderr, "leaf_consistent, %d\n", fc_strategy);
#endif

	switch (fc_strategy)
	{
		case RTLeftStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_left, fc_key, fc_query);
			break;
		case RTOverLeftStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_over_left, fc_key, fc_query);
			break;
		case RTOverlapStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_overlap, fc_key, fc_query);
			break;
		case RTOverRightStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_over_right, fc_key, fc_query);
			break;
		case RTRightStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_right, fc_key, fc_query);
			break;
		case RTSameStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_same, fc_key, fc_query);
			break;
		case RTContainsStrategyNumber:
		case RTOldContainsStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_contains, fc_key, fc_query);
			break;
		case RTContainedByStrategyNumber:
		case RTOldContainedByStrategyNumber:
			fc_retval = DirectFunctionCall2(seg_contained, fc_key, fc_query);
			break;
		default:
			fc_retval = false;
	}

	PG_RETURN_DATUM(fc_retval);
}

static Datum fc_gseg_internal_consistent(Datum fc_key, Datum fc_query, StrategyNumber fc_strategy)
{
	bool		fc_retval;

#ifdef GIST_QUERY_DEBUG
	fprintf(stderr, "internal_consistent, %d\n", fc_strategy);
#endif

	switch (fc_strategy)
	{
		case RTLeftStrategyNumber:
			fc_retval =
				!DatumGetBool(DirectFunctionCall2(seg_over_right, fc_key, fc_query));
			break;
		case RTOverLeftStrategyNumber:
			fc_retval =
				!DatumGetBool(DirectFunctionCall2(seg_right, fc_key, fc_query));
			break;
		case RTOverlapStrategyNumber:
			fc_retval =
				DatumGetBool(DirectFunctionCall2(seg_overlap, fc_key, fc_query));
			break;
		case RTOverRightStrategyNumber:
			fc_retval =
				!DatumGetBool(DirectFunctionCall2(seg_left, fc_key, fc_query));
			break;
		case RTRightStrategyNumber:
			fc_retval =
				!DatumGetBool(DirectFunctionCall2(seg_over_left, fc_key, fc_query));
			break;
		case RTSameStrategyNumber:
		case RTContainsStrategyNumber:
		case RTOldContainsStrategyNumber:
			fc_retval =
				DatumGetBool(DirectFunctionCall2(seg_contains, fc_key, fc_query));
			break;
		case RTContainedByStrategyNumber:
		case RTOldContainedByStrategyNumber:
			fc_retval =
				DatumGetBool(DirectFunctionCall2(seg_overlap, fc_key, fc_query));
			break;
		default:
			fc_retval = false;
	}

	PG_RETURN_BOOL(fc_retval);
}

static Datum fc_gseg_binary_union(Datum fc_r1, Datum fc_r2, int *fc_sizep)
{
	Datum		fc_retval;

	fc_retval = DirectFunctionCall2(seg_union, fc_r1, fc_r2);
	*fc_sizep = sizeof(SEG);

	return fc_retval;
}


Datum seg_contains(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL((fc_a->lower <= fc_b->lower) && (fc_a->upper >= fc_b->upper));
}

Datum seg_contained(PG_FUNCTION_ARGS)
{
	Datum		fc_a = PG_GETARG_DATUM(0);
	Datum		fc_b = PG_GETARG_DATUM(1);

	PG_RETURN_DATUM(DirectFunctionCall2(seg_contains, fc_b, fc_a));
}

/*****************************************************************************
 * R-tree 索引的操作符类
 *****************************************************************************/

Datum seg_same(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp == 0);
}

/*	seg_overlap -- a 是否与 b 重叠?
 */
Datum seg_overlap(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL(((fc_a->upper >= fc_b->upper) && (fc_a->lower <= fc_b->upper)) ||
				   ((fc_b->upper >= fc_a->upper) && (fc_b->lower <= fc_a->upper)));
}

/*	seg_over_left -- (a) 的右边缘位于 (b) 的右边缘左侧或重合吗?
 */
Datum seg_over_left(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL(fc_a->upper <= fc_b->upper);
}

/*	seg_left -- (a) 完全位于 (b) 的左侧吗?
 */
Datum seg_left(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL(fc_a->upper < fc_b->lower);
}

/*	seg_right -- (a) 完全位于 (b) 的右侧吗?
 */
Datum seg_right(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL(fc_a->lower > fc_b->upper);
}

/*	seg_over_right -- (a) 的左边缘位于 (b) 的左边缘右侧或重合吗?
 */
Datum seg_over_right(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	PG_RETURN_BOOL(fc_a->lower >= fc_b->lower);
}

Datum seg_union(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);
	SEG		   *fc_n;

	fc_n = (SEG *) palloc(sizeof(*fc_n));

	/* 取上限的最大值 */
	if (fc_a->upper > fc_b->upper)
	{
		fc_n->upper = fc_a->upper;
		fc_n->u_sigd = fc_a->u_sigd;
		fc_n->u_ext = fc_a->u_ext;
	}
	else
	{
		fc_n->upper = fc_b->upper;
		fc_n->u_sigd = fc_b->u_sigd;
		fc_n->u_ext = fc_b->u_ext;
	}

	/* 取下限的最小值 */
	if (fc_a->lower < fc_b->lower)
	{
		fc_n->lower = fc_a->lower;
		fc_n->l_sigd = fc_a->l_sigd;
		fc_n->l_ext = fc_a->l_ext;
	}
	else
	{
		fc_n->lower = fc_b->lower;
		fc_n->l_sigd = fc_b->l_sigd;
		fc_n->l_ext = fc_b->l_ext;
	}

	PG_RETURN_POINTER(fc_n);
}

Datum seg_inter(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);
	SEG		   *fc_n;

	fc_n = (SEG *) palloc(sizeof(*fc_n));

	/* 取上限的最小值 */
	if (fc_a->upper < fc_b->upper)
	{
		fc_n->upper = fc_a->upper;
		fc_n->u_sigd = fc_a->u_sigd;
		fc_n->u_ext = fc_a->u_ext;
	}
	else
	{
		fc_n->upper = fc_b->upper;
		fc_n->u_sigd = fc_b->u_sigd;
		fc_n->u_ext = fc_b->u_ext;
	}

	/* 取下限的最大值 */
	if (fc_a->lower > fc_b->lower)
	{
		fc_n->lower = fc_a->lower;
		fc_n->l_sigd = fc_a->l_sigd;
		fc_n->l_ext = fc_a->l_ext;
	}
	else
	{
		fc_n->lower = fc_b->lower;
		fc_n->l_sigd = fc_b->l_sigd;
		fc_n->l_ext = fc_b->l_ext;
	}

	PG_RETURN_POINTER(fc_n);
}

static void fc_rt_seg_size(SEG *fc_a, float *fc_size)
{
	if (fc_a == (SEG *) NULL || fc_a->upper <= fc_a->lower)
		*fc_size = 0.0;
	else
		*fc_size = (float) Abs(fc_a->upper - fc_a->lower);
}

Datum seg_size(PG_FUNCTION_ARGS)
{
	SEG		   *fc_seg = PG_GETARG_SEG_P(0);

	PG_RETURN_FLOAT4((float) Abs(fc_seg->upper - fc_seg->lower));
}


/*****************************************************************************
 *				   杂项操作符
 *****************************************************************************/
Datum seg_cmp(PG_FUNCTION_ARGS)
{
	SEG		   *fc_a = PG_GETARG_SEG_P(0);
	SEG		   *fc_b = PG_GETARG_SEG_P(1);

	/*
	 * 首先比较下边界位置
	 */
	if (fc_a->lower < fc_b->lower)
		PG_RETURN_INT32(-1);
	if (fc_a->lower > fc_b->lower)
		PG_RETURN_INT32(1);

	/*
	 * a->lower == b->lower，因此考虑边界类型。
	 *
	 * 一个 '-' 下限小于任何其他类型（这只有在 -HUGE_VAL 用作常规数据值时才相关）。一个 '<' 下限小于任何其他类型但 '-'。一个 '>' 下限大于任何其他类型。
	 */
	if (fc_a->l_ext != fc_b->l_ext)
	{
		if (fc_a->l_ext == '-')
			PG_RETURN_INT32(-1);
		if (fc_b->l_ext == '-')
			PG_RETURN_INT32(1);
		if (fc_a->l_ext == '<')
			PG_RETURN_INT32(-1);
		if (fc_b->l_ext == '<')
			PG_RETURN_INT32(1);
		if (fc_a->l_ext == '>')
			PG_RETURN_INT32(1);
		if (fc_b->l_ext == '>')
			PG_RETURN_INT32(-1);
	}

	/*
	 * 对于其他边界类型，首先考虑有效数字的位数。
	 */
	if (fc_a->l_sigd < fc_b->l_sigd)	/* (a) 模糊，可能包含 (b) */
		PG_RETURN_INT32(-1);
	if (fc_a->l_sigd > fc_b->l_sigd)	/* (a) 较不模糊，可能包含在 (b) 中 */
		PG_RETURN_INT32(1);

	/*
	 * 对于相同的位数，近似边界比精确边界更模糊。
	 */
	if (fc_a->l_ext != fc_b->l_ext)
	{
		if (fc_a->l_ext == '~')	/* (a) 是近似的，而 (b) 是精确的 */
			PG_RETURN_INT32(-1);
		if (fc_b->l_ext == '~')
			PG_RETURN_INT32(1);
		/* 不能在这里，除非数据损坏 */
		elog(ERROR, "bogus lower boundary types %d %d",
			 (int) fc_a->l_ext, (int) fc_b->l_ext);
	}

	/* 此时，下边界是相同的 */

	/*
	 * 首先比较上边界位置
	 */
	if (fc_a->upper < fc_b->upper)
		PG_RETURN_INT32(-1);
	if (fc_a->upper > fc_b->upper)
		PG_RETURN_INT32(1);

	/*
	 * a->upper == b->upper，因此考虑边界类型。
	 *
	 * 一个 '-' 上限大于任何其他类型（这只有在 HUGE_VAL 用作常规数据值时才相关）。一个 '<' 上限小于任何其他类型。一个 '>' 上限大于任何其他类型，但 '-'。
	 */
	if (fc_a->u_ext != fc_b->u_ext)
	{
		if (fc_a->u_ext == '-')
			PG_RETURN_INT32(1);
		if (fc_b->u_ext == '-')
			PG_RETURN_INT32(-1);
		if (fc_a->u_ext == '<')
			PG_RETURN_INT32(-1);
		if (fc_b->u_ext == '<')
			PG_RETURN_INT32(1);
		if (fc_a->u_ext == '>')
			PG_RETURN_INT32(1);
		if (fc_b->u_ext == '>')
			PG_RETURN_INT32(-1);
	}

	/*
	 * 对于其他边界类型，首先考虑有效数字的位数。注意，此处结果与下边界情况相反。
	 */
	if (fc_a->u_sigd < fc_b->u_sigd)	/* (a) 模糊，可能包含 (b) */
		PG_RETURN_INT32(1);
	if (fc_a->u_sigd > fc_b->u_sigd)	/* (a) 较不模糊，可能包含在 (b) 中 */
		PG_RETURN_INT32(-1);

	/*
	 * 对于相同的位数，近似边界比精确边界更模糊。再次，结果与下边界情况相反。
	 */
	if (fc_a->u_ext != fc_b->u_ext)
	{
		if (fc_a->u_ext == '~')	/* (a) 是近似的，而 (b) 是精确的 */
			PG_RETURN_INT32(1);
		if (fc_b->u_ext == '~')
			PG_RETURN_INT32(-1);
		/* 不能在这里，除非数据损坏 */
		elog(ERROR, "bogus upper boundary types %d %d",
			 (int) fc_a->u_ext, (int) fc_b->u_ext);
	}

	PG_RETURN_INT32(0);
}

Datum seg_lt(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp < 0);
}

Datum seg_le(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp <= 0);
}

Datum seg_gt(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp > 0);
}

Datum seg_ge(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp >= 0);
}


Datum seg_different(PG_FUNCTION_ARGS)
{
	int			fc_cmp = DatumGetInt32(DirectFunctionCall2(seg_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_cmp != 0);
}



/*****************************************************************************
 *				   辅助函数
 *****************************************************************************/

/*
 * 该例程的目的在于以精确 n 位有效数字打印给定的浮点值。它的行为
 * 类似于 %.ng ，但在这里它打印 8.00，而 %.ng 会打印 8。
 * 返回在 "result" 中写入的字符串长度。
 *
 * 调用者必须提供一个足够大的结果缓冲区；16 字节
 * 应该足够适用于所有已知的浮点实现。
 */
static int fc_restore(char *fc_result, float fc_val, int fc_n)
{
	char		fc_buf[25] = {
		'0', '0', '0', '0', '0',
		'0', '0', '0', '0', '0',
		'0', '0', '0', '0', '0',
		'0', '0', '0', '0', '0',
		'0', '0', '0', '0', '\0'
	};
	char	   *fc_p;
	int			fc_exp;
	int			fc_i,
				fc_dp,
				fc_sign;

	/*
	 * 限制有效数字的数量以避免输出中的无效信息，并确保我们不会超出结果缓冲区的范围。 (n 不应为负数，但检查以保护我们免受损坏数据的影响。)
	 */
	if (fc_n <= 0)
		fc_n = FLT_DIG;
	else
		fc_n = Min(fc_n, FLT_DIG);

	/* 记住符号 */
	fc_sign = (fc_val < 0 ? 1 : 0);

	/* 打印，最初使用 %e 风格 */
	sprintf(fc_result, "%.*e", fc_n - 1, fc_val);

	/* 找到指数 */
	fc_p = strchr(fc_result, 'e');

	/* 如果我们有 'inf' 或类似情况则抛出 */
	if (fc_p == NULL)
		return strlen(fc_result);

	fc_exp = atoi(fc_p + 1);
	if (fc_exp == 0)
	{
		/* 只是截断 'e+00' */
		*fc_p = '\0';
	}
	else
	{
		if (Abs(fc_exp) <= 4)
		{
			/*
			 * 从尾数中去掉小数点并将数字写入 buf 数组
			 */
			for (fc_p = fc_result + fc_sign, fc_i = 10, fc_dp = 0; *fc_p != 'e'; fc_p++, fc_i++)
			{
				fc_buf[fc_i] = *fc_p;
				if (*fc_p == '.')
				{
					fc_dp = fc_i--;	/* 跳过小数点 */
				}
			}
			if (fc_dp == 0)
				fc_dp = fc_i--;		/* 在上述 for() 循环中未找到小数点 */

			if (fc_exp > 0)
			{
				if (fc_dp - 10 + fc_exp >= fc_n)
				{
					/*
					 * 小数点在最后一个有效数字之后；
					 * 之间的数字必须转换为指数
					 * 小数点放在第一个数字之后
					 */
					fc_exp = fc_dp - 10 + fc_exp - fc_n;
					fc_buf[10 + fc_n] = '\0';

					/* 插入小数点 */
					if (fc_n > 1)
					{
						fc_dp = 11;
						for (fc_i = 23; fc_i > fc_dp; fc_i--)
							fc_buf[fc_i] = fc_buf[fc_i - 1];
						fc_buf[fc_dp] = '.';
					}

					/*
					 * 根据小数点后数字的数量调整指数
					 */
					if (fc_n > 1)
						sprintf(&fc_buf[11 + fc_n], "e%d", fc_exp + fc_n - 1);
					else
						sprintf(&fc_buf[11], "e%d", fc_exp + fc_n - 1);

					if (fc_sign)
					{
						fc_buf[9] = '-';
						strcpy(fc_result, &fc_buf[9]);
					}
					else
						strcpy(fc_result, &fc_buf[10]);
				}
				else
				{				/* 插入小数点 */
					fc_dp += fc_exp;
					for (fc_i = 23; fc_i > fc_dp; fc_i--)
						fc_buf[fc_i] = fc_buf[fc_i - 1];
					fc_buf[11 + fc_n] = '\0';
					fc_buf[fc_dp] = '.';
					if (fc_sign)
					{
						fc_buf[9] = '-';
						strcpy(fc_result, &fc_buf[9]);
					}
					else
						strcpy(fc_result, &fc_buf[10]);
				}
			}
			else
			{					/* exp <= 0 */
				fc_dp += fc_exp - 1;
				fc_buf[10 + fc_n] = '\0';
				fc_buf[fc_dp] = '.';
				if (fc_sign)
				{
					fc_buf[fc_dp - 2] = '-';
					strcpy(fc_result, &fc_buf[fc_dp - 2]);
				}
				else
					strcpy(fc_result, &fc_buf[fc_dp - 1]);
			}
		}

		/* 对于 Abs(exp) > 4 不做任何操作； %e 必须有效 */
		/* 只是去掉 [eE]- 后的零以及 [Ee] 后的 +零。 */

		/* ... 这还没有完成。 */
	}
	return strlen(fc_result);
}


/*
** 杂项
*/

/* 找出表示浮点数的字符串中的有效数字数量 */
int significant_digits(const char *fc_s)
{
	const char *fc_p = fc_s;
	int			fc_n,
				fc_c,
				fc_zeroes;

	fc_zeroes = 1;
	/* 跳过前导零和符号 */
	for (fc_c = *fc_p; (fc_c == '0' || fc_c == '+' || fc_c == '-') && fc_c != 0; fc_c = *(++fc_p));

	/* 跳过小数点和后面的零 */
	for (fc_c = *fc_p; (fc_c == '0' || fc_c == '.') && fc_c != 0; fc_c = *(++fc_p))
	{
		if (fc_c != '.')
			fc_zeroes++;
	}

	/* 计算有效数字 (n) */
	for (fc_c = *fc_p, fc_n = 0; fc_c != 0; fc_c = *(++fc_p))
	{
		if (!((fc_c >= '0' && fc_c <= '9') || (fc_c == '.')))
			break;
		if (fc_c != '.')
			fc_n++;
	}

	if (!fc_n)
		return fc_zeroes;

	return fc_n;
}
